<?php
//=======================================
//###################################
// Kayako Web Solutions
//
// Source Copyright 2001-2004 Kayako Web Solutions
// Unauthorized reproduction is not allowed
// License Number: %LICENSE%%%
// $Author: vshoor $ ($Date: 2006/07/24 22:11:25 $)
// $RCSfile: functions_xml.php,v $ : $Revision: 1.9 $ 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//###################################
//=======================================


/**
* XML Handler class, used to parse and write XML data, Partial code taken from comments of xml_parse_into_struct() function of PHP Manual. Credit where its due.
*/
class XML
{

	var $xmldata = array();
	var $indent = 0;
	var $lasttag = 0;
	var $crlf = "\r\n";
	var $charset = "UTF-8";

	/**
	* Constructor, runs buildXML()
	*/
	function XML()
	{
		$this->buildXML();
	}

	/**
	* Converts XML data to a structured array
	*/
	function xml2tree($string)
	{
		$parser = xml_parser_create();
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

		xml_parse_into_struct($parser, $string, $vals, $index);
		xml_parser_free($parser);

		$stack = array(array());
		$stacktop = 0;
		$parent = array();
		foreach($vals as $val)
		{
			$type = $val['type'];
		
			if($type == 'open' || $type == 'complete')
			{
				// open tag
				$stack[$stacktop++] = $tagi;
				$tagi = array('tag' => $val['tag']);

				if(isset($val['attributes']))
				{
					$tagi['attrs'] = $val['attributes'];
				}

				if(isset($val['value']) && trim($val["value"]) != "")
				{
					$tagi['values'][] = $val['value'];
				}
			}

			if($type == 'complete' || $type == 'close')
			{
				// finish tag
				$tags[] = $oldtagi = $tagi; 
				$tagi = $stack[--$stacktop];
				$oldtag = $oldtagi['tag'];  
				unset($oldtagi['tag']);
				$tagi['children'][$oldtag][] = $oldtagi;
				$parent = $tagi;
			}
			
			if($type=='cdata' && trim($val["value"]) != "")
			{
				$tagi['values'][] = $val['value'];
			}
		}
		return $parent['children'];
	}

	/**
	* Returns an array of XML elements from a file
	*/
	function xml2treefile($file)
	{
		return $this->xml2tree(implode("",file($file)));
	}

	/**
	* Function to be called before XML building starts
	*/
	function buildXML()
	{
		global $_SWIFT;

		if (SWIFT_AREA == SWIFT_MOBILE)
		{
			$this->charset = "UTF-16";
		} else if (!empty($_SWIFT["language"]["charset"])) {
			$this->charset = $_SWIFT["language"]["charset"];
		}

		$this->xmldata = array();
		$this->xmldata[] = '<?xml version="1.0" encoding="'. $this->charset .'"?>'.$this->crlf;
		//<?
		return true;
	}

	/**
	* Sets the default XML Characterset
	*/
	function setCharset($charset)
	{
		$this->charset = $charset;

		return true;
	}

	/**
	* Sets CRLF, should be either \r\n or \n
	*/
	function setCRLF($type)
	{
		$this->crlf = $type;

		return true;
	}

	/**
	* Returns the data for a parentTag
	*/
	function returnParentTag($tagname, $attrs = array())
	{
		foreach ($attrs as $key=>$val)
		{
			$attrsval .= " ".$key.'="'.$val.'"';
		}

		$retval = str_repeat("	",$this->indent)."<".$tagname.$attrsval.">".$this->crlf;
		$this->indent++;

		return $retval;
	}

	/**
	* Adds a Parent Tag
	*/
	function addParentTag($tagname, $attrs = array())
	{
		$this->xmldata[] = $this->returnParentTag($tagname, $attrs);

		return true;
	}

	/**
	* Returns tag data
	*/
	function returnTag($tagname, $value, $attrs = array())
	{
		foreach ($attrs as $key=>$val)
		{
			$attrsval .= " ".$key.'="'.$val.'"';
		}

		// Bad coding practice to hard code it according to area, I know.. but I am lazy!
		if (SWIFT_AREA == SWIFT_SYNCML || SWIFT_AREA == SWIFT_SYNCWORKS || SWIFT_AREA == SWIFT_MOBILE)
		{
			return str_repeat("	",$this->indent)."<".$tagname.$attrsval."><![CDATA[". $value ."]]></".$tagname.">".$this->crlf;
		} else {
			return str_repeat("	",$this->indent)."<".$tagname.$attrsval."><![CDATA[".iif(empty($value), "NULL", $value)."]]></".$tagname.">".$this->crlf;
		}
	}

	/**
	* Starts a tag
	*/
	function addTag($tagname, $value, $attrs = array())
	{
		$this->xmldata[] = $this->returnTag($tagname, $value, $attrs);
	
		return true;
	}

	/**
	* Returns an Ending Tag Data
	*/
	function returnEndTag($tagname)
	{
		$this->indent--;
		$retval = str_repeat("	",$this->indent)."</".$tagname.">".$this->crlf;
		
		return $retval;
	}
	
	/**
	* Ends an XML Tag, should be called if no $value is specified in starTag
	*/
	function endTag($tagname)
	{
		$this->xmldata[] = $this->returnEndTag($tagname);

		return true;
	}

	/**
	* Alias for endTag
	*/
	function endParentTag($tagname)
	{
		return $this->endTag($tagname);
	}

	/**
	* Add an XML Comment
	*/
	function addComment($comment)
	{
		$this->xmldata[] = str_repeat("	",$this->indent)."<!-- ".$comment." -->".$this->crlf;;		
	}

	/**
	* Returns XML Data as single string
	*/
	function returnXML()
	{
		return implode("",$this->xmldata);
	}

	/**
	* Returns XML Data as Array
	*/
	function returnXMLArray()
	{
		return $this->xmldata;
	}

	/**
	* Spits out XML Data
	*/
	function echoXML()
	{
		echo trim($this->returnXML());
	}

	/**
	* Spits out Compressed/Uncompressed XML Data according to specifications of winapp
	*/
	function echoXMLWinapp()
	{
		echoWinappData(trim($this->returnXML()));
	}

	/**
	* UTF DATA
	*/
	function echoXMLUTFWinapp()
	{
		$_data = trim($this->returnXML());

		echoWinappData($_data);
	}
}

?>